package cn.xiongxf.service.impl;

import cn.xiongxf.constant.RedisKeyConstant;
import cn.xiongxf.domain.Login;
import cn.xiongxf.domain.User;
import cn.xiongxf.domain.UserRecommendCode;
import cn.xiongxf.dto.RedisValueDto;
import cn.xiongxf.dto.UserRegisterDto;
import cn.xiongxf.exception.GlobalCustomException;
import cn.xiongxf.feign.LoginFeignClient;
import cn.xiongxf.mapper.UserMapper;
import cn.xiongxf.result.JsonResult;
import cn.xiongxf.service.IUserAccountService;
import cn.xiongxf.service.IUserBaseInfoService;
import cn.xiongxf.service.IUserRecommendCodeService;
import cn.xiongxf.service.IUserService;
import cn.xiongxf.util.BitStatesUtils;
import cn.xiongxf.util.CodeGenerateUtils;
import cn.xiongxf.util.QRCodeUtil;
import cn.xiongxf.util.StrUtils;
import cn.xiongxf.vo.RecommendCodeVO;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author xiongxiaofu
 * @since 2022-08-12
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private IUserAccountService userAccountService;
    @Autowired
    private IUserBaseInfoService userBaseInfoService;
    @Autowired
    private LoginFeignClient loginFeignClient;
    @Autowired
    private IUserRecommendCodeService userRecommendCodeService;

    /**
     * 用户手机短信注册
     * @param userRegisterDto
     */
    @Override
    // 分布式(全局)事务注解,加在调用方,被调用方不加
    @GlobalTransactional(rollbackFor = Exception.class)
    public void register(UserRegisterDto userRegisterDto) {
        // 1.参数校验(JSR303已做)

        // 2.条件校验
        registerValidation(userRegisterDto);

        // 3.保存t_login
        Long loginId = saveLogin(userRegisterDto.getMobile(), userRegisterDto.getPassword());

        // 4.保存t_user
        User user = save(userRegisterDto.getMobile(), loginId);

        // 5.保存t_user_account
        userAccountService.save(user.getId());

//        int i = 1/0;  // 校验分布式事务

        // 6.保存t_user_base_info
        userBaseInfoService.save(user.getId(),userRegisterDto.getRegChannel(),userRegisterDto.getRecommendCode());

    }

    // 抽取方法:注册条件校验
    private void registerValidation(UserRegisterDto userRegisterDto) {
        // 2.1.短信验证码是否正确
        String key = RedisKeyConstant.SMSCODE_KEY_PREFIX + userRegisterDto.getMobile();
        RedisValueDto redisValueDto = (RedisValueDto) redisTemplate.opsForValue().get(key);
        if (redisValueDto == null)   throw new GlobalCustomException("短信验证码已过期,请重新获取");
        if (! userRegisterDto.getSmsCode().equals(redisValueDto.getSmsCode()))   throw new GlobalCustomException("短信验证码不正确");

        // 2.2.手机号是否被注册
        Wrapper<User> query = new EntityWrapper<>();
        query.eq("phone", userRegisterDto.getMobile());  // 相当于sql中的 where phone = #{phone}
        User userDB = selectOne(query);
        if (userDB != null)   throw new GlobalCustomException("手机号已被注册");
    }

    // 抽取方法:保存login
    private Long saveLogin(String phone,String password) {
        Login login = new Login();
        login.setUsername(phone);
        login.setPassword(password);
        login.setType(Login.USER_LOGIN_TYPE);  // 1
        JsonResult jsonResult = loginFeignClient.save(login);  // 使用OpenFeign调用service-uaa服务接口
        // boolean字段的getter方法:isXxx
        if (! jsonResult.isSuccess())   throw new GlobalCustomException(JsonResult.error().getMessage());
        if (jsonResult.getData() == null)   throw new GlobalCustomException("登陆信息保存失败");
//        return (Long) jsonResult.getData();  // ClassCastException:Long用byte编译后再转换回来,成了Inteager
        return Long.valueOf(jsonResult.getData().toString());  // Integer转String转Long
    }

    // 抽取方法:保存user
    private User save(String phone, long loginId) {
        User user = new User();
        user.setCreateTime(System.currentTimeMillis());
        user.setPhone(phone);
        user.setNickName(phone);

        // 保存位状态
        long bitState = BitStatesUtils.addState(0L, BitStatesUtils.OP_REGISTED);
        bitState = BitStatesUtils.addState(bitState, BitStatesUtils.OP_ACTIVED);
        bitState = BitStatesUtils.addState(bitState, BitStatesUtils.OP_AUTHED_PHONE);
        user.setBitState(bitState);

        user.setSecLevel(0);
        user.setLoginId(loginId);
        insert(user);
        return user;
    }

    /**
     * 根据用户ID生成推存码及二维码并保存到DB
     */
    @Override
    public RecommendCodeVO recommendCodeForUser(Long userId) {
        // 从Redis中查询此用户的推荐码
        String recommendCode = (String) redisTemplate.opsForValue().get(RedisKeyConstant.RECOMMEND_CODE_KEY + userId);
        if (recommendCode == null){
            // Redis中没有此用户的推荐码
            // 生成推荐码
            recommendCode = StrUtils.getComplexRandomString(4);
            // 将推荐码保存到DB
            UserRecommendCode userRecommendCode = new UserRecommendCode();
            userRecommendCode.setUserId(userId);
            userRecommendCode.setRecommendCode(recommendCode);
            userRecommendCodeService.insert(userRecommendCode);
            // 将推荐码保存到Redis（key根据userId动态配置）
            redisTemplate.opsForValue().set(RedisKeyConstant.RECOMMEND_CODE_KEY + userId, recommendCode);
        }

        // 调用工具类生成二维码
        String qrCodeBase64 = QRCodeUtil.getQRCodeBase64(
                "http://localhost:6003/reg.phone.html?code=" + recommendCode, 100, 100);

        // 返回推荐码和二维码封装的VO对象
        return new RecommendCodeVO(recommendCode, qrCodeBase64);
    }

}
